#include "D3D9RenderSystem.h"
#include "D3D9VertexBuffer.h"
#include "D3D9IndexBuffer.h"
#include "D3D9HLSLProgram.h"
#include "CommonFunction.h"

namespace DreamEngineD3D
{

IDirect3D9*	CD3D9RenderSystem::m_pd3d = MATH_NULL;

CD3D9RenderSystem::CD3D9RenderSystem(void)
{
}


CD3D9RenderSystem::~CD3D9RenderSystem(void)
{
}

Error CD3D9RenderSystem::Initialize()
{
	m_pd3d = Direct3DCreate9(D3D_SDK_VERSION);
	if (m_pd3d == MATH_NULL)
	{
		return ERROR_CREATE_D3D_FAILD;
	}

	return ERROR_OK;
}

Error CD3D9RenderSystem::ShutDown()
{
	SAFE_RELEASE(m_pd3d);

	return ERROR_OK;
}

CRenderWindow* CD3D9RenderSystem::CreateRenderWindow(const CString& name,\
	m_uint16 width, m_uint16 heigth)
{
	CD3D9RenderWindow* pRenderWindow = new CD3D9RenderWindow(name);
	pRenderWindow->Initialize(width, heigth);

	m_pActiveWindow = pRenderWindow;
	m_renderWindows.push_back(pRenderWindow);

	return pRenderWindow;

}

void CD3D9RenderSystem::BeginFrame()
{
	LPDIRECT3DDEVICE9 pD3DDevice = static_cast<CD3D9RenderWindow*>(m_pActiveWindow)\
		->GetDevice()->GetD3DDevice();

	CViewport* pViewport = m_pActiveWindow->GetViewport();
	if (pViewport!=MATH_NULL && pViewport->IsChanged())
	{
		_SetViewport(pViewport);
		pViewport->SetChanged(false);
	}

	if (pD3DDevice != MATH_NULL)
	{ 
		pD3DDevice->Clear( 0, MATH_NULL, D3DCLEAR_TARGET, 0x00ffff00, 1.0f, 0 );
		pD3DDevice->BeginScene();
	}
	
}

void CD3D9RenderSystem::EndFrame()
{
	LPDIRECT3DDEVICE9 pD3DDevice = static_cast<CD3D9RenderWindow*>(m_pActiveWindow)\
		->GetDevice()->GetD3DDevice();

	if (pD3DDevice != MATH_NULL)
	{
		pD3DDevice->EndScene();
		pD3DDevice->Present( MATH_NULL, MATH_NULL, MATH_NULL, MATH_NULL );
	}
}

void CD3D9RenderSystem::Render(CRenderData* pRenderData)
{
	if (pRenderData==MATH_NULL || pRenderData->IsEmpty()) return;
	
	LPDIRECT3DDEVICE9 pDevice = GetActiveDevice()->GetD3DDevice();
	if (pDevice==MATH_NULL)return;

	_SetRenderState(pRenderData->m_pMaterial);
	_ActiveVertexBuffer(pRenderData);
	_BuildShader(pRenderData->m_pMaterial);
	_BulidDeclaration(pRenderData->GetElementLst());
	_Draw(pRenderData);
}

CD3D9Device* CD3D9RenderSystem::GetActiveDevice()const
{
	CD3D9RenderWindow* pD3DRenderWindow = static_cast<CD3D9RenderWindow*>(m_pActiveWindow);
	return pD3DRenderWindow->GetDevice();
}

void CD3D9RenderSystem::_SetViewport(CViewport* pViewport)
{
	if (pViewport==MATH_NULL)return;

	LPDIRECT3DDEVICE9  pDevice = (static_cast<CD3D9RenderWindow*>(m_pActiveWindow)->GetDevice())->GetD3DDevice();
	
	D3DVIEWPORT9 vp;
	vp.Height	= DWORD(pViewport->GetHeight());
	vp.Width	= DWORD(pViewport->GetWidth());
	vp.MaxZ		= pViewport->GetMaxZ();
	vp.MinZ		= pViewport->GetMinZ();
	vp.X		= pViewport->GetLeft();
	vp.Y		= pViewport->GetTop();

	pDevice->SetViewport(&vp);
}

void CD3D9RenderSystem::_SetRenderState(CMaterial* pMaterial)
{
	if (pMaterial==MATH_NULL)return;

	LPDIRECT3DDEVICE9 pDevice = GetActiveDevice()->GetD3DDevice();
	pDevice->SetRenderState(D3DRS_ZENABLE, pMaterial->m_isZTestEnble);
	pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL(pMaterial->m_cullMode));	
	pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILLMODE(pMaterial->m_fillMode));
}

void CD3D9RenderSystem::_ActiveVertexBuffer(CRenderData* pRenderData)
{
	if (pRenderData==MATH_NULL)return;

	CRenderData::VertexStream::iterator iter;
	for (iter=pRenderData->m_vertexStream.begin(); 
		iter!=pRenderData->m_vertexStream.end(); ++iter)
	{
		iter->second->Active(iter->first);
	}
}

void CD3D9RenderSystem::_BuildShader(CMaterial* pMaterial)
{
	if (pMaterial==MATH_NULL) return;

	LPDIRECT3DDEVICE9 pDevice = GetActiveDevice()->GetD3DDevice();

	CD3D9HLSLVertex*	pVertex = static_cast<CD3D9HLSLVertex*>
		(pMaterial->GetVertexProgram());
	CD3D9HLSLFragment*	pFragment = static_cast<CD3D9HLSLFragment*>
		(pMaterial->GetFragmentProgram());


	IDirect3DVertexShader9* pVertexShader = pVertex?pVertex->GetD3D9Shader():MATH_NULL;
	IDirect3DPixelShader9*	pPixelShader =  pFragment?pFragment->GetD3D9Shader():MATH_NULL;
	pDevice->SetVertexShader(pVertexShader);
	pDevice->SetPixelShader(pPixelShader);

	pVertex->Update();
	pFragment->Update();
	
}

void CD3D9RenderSystem::_BulidDeclaration(const CVertexBuffer::ElementLst& elementLst)
{
	CD3D9Device* pDevice = GetActiveDevice();

	if (pDevice != MATH_NULL)
	{
		IDirect3DVertexDeclaration9* pDecl = MATH_NULL;
		pDevice->CreateVertexDeclaration(pDecl, elementLst);
		pDevice->GetD3DDevice()->SetVertexDeclaration(pDecl);
	}
}

void CD3D9RenderSystem::_Draw(CRenderData* pRenderData)
{
	if (pRenderData==MATH_NULL || pRenderData->IsEmpty())return;

	LPDIRECT3DDEVICE9 pDevice = GetActiveDevice()->GetD3DDevice();

	
	if (!pRenderData->IsUseIndexBuffer())
	{
		D3DPRIMITIVETYPE priType =
			CCommonFunction::HDBufTypeToD3DPriType(pRenderData->GetRenderType(0));

		m_uint8 streamNum = m_uint8(pRenderData->m_vertexStream.size());
		pDevice->DrawPrimitive(priType, 0, pRenderData->GetElemCount(0));
	}
	else
	{
		for (m_uint16 i=0; i<pRenderData->GetPatch(); ++i)
		{
			D3DPRIMITIVETYPE priType =
				CCommonFunction::HDBufTypeToD3DPriType(pRenderData->GetRenderType(i));

			pRenderData->GetIndexBuffer(i)->Active();

			pDevice->DrawIndexedPrimitive(priType, 0, 0,
				pRenderData->GetVertexBuffer(0)->DataCount(), 0, pRenderData->GetElemCount(i));
		}
	}
}
}